// TODO: make this a shared lib. See https://ziggit.dev/t/how-to-package-a-zig-source-module-and-how-to-use-it/3457


const std = @import("std");
const curses = @cImport({
	@cInclude("ncurses.h");
});

const constants = @import("constants.zig");


pub fn Grid(comptime CharType: type) type {
	return struct {
		width: u8,
		height: u8,
		empty_char: CharType,
		empty_color_pair_id: u8,
		window: *curses.struct__win_st,
		allocator: std.mem.Allocator = std.heap.page_allocator,
		_grid: [][]Cell(CharType) = undefined,

		const Self = @This();


		pub fn init(self: *Self) !void {
			const cell = Cell(CharType){
				.char = self.empty_char,
				.color_pair_id = self.empty_color_pair_id,
			};
			const rows: [][]Cell(CharType) = try self.allocator.alloc(
					[]Cell(CharType), self.height);
			for (0..rows.len) |i| {
				rows[i] = try self.allocator.alloc(Cell(CharType), self.width);
				@memset(rows[i], cell);
			}

			self._grid = rows;
		}


		pub fn deinit(self: *Self) void {
			for (0..self._grid.len) |i| {
				self.allocator.free(self._grid[i]);
			}
			self.allocator.free(self._grid);
			self._grid = &.{};
		}


		pub fn display(self: *Self) !void {
			const left_pad: c_int = @divFloor(curses.getmaxx(self.window), 2)
					- self.width;
			const top_pad: c_int = @divFloor(
					curses.getmaxy(self.window) - self.height, 2);

			for (self._grid, 0..) |row, row_idx| {
				const r_idx: c_int = @intCast(row_idx);
				_ = curses.move(r_idx + top_pad, left_pad);

				for (row) |cell| {
					Self.print_cell(&cell);

					const pipe_horiz_char = @intFromEnum(
							constants.CellChar.PIPE_HORIZ);
					const pipe_corner_1_char = @intFromEnum(
							constants.CellChar.PIPE_CORNER_1);
					const pipe_corner_4_char = @intFromEnum(
							constants.CellChar.PIPE_CORNER_4);
					const pipe_end_1_char = @intFromEnum(
							constants.CellChar.PIPE_END_1);
					const normal_color_id = @intFromEnum(
							constants.CellColor.NORMAL);

					var cell_to_print: *const Cell(CharType) = undefined;
					if (cell.char == pipe_horiz_char) {
						cell_to_print = &cell;
					} else if (cell.char == pipe_corner_1_char) {
						cell_to_print = &Cell(CharType){
							.char = @intFromEnum(constants.CellChar.PIPE_HORIZ),
							.color_pair_id = cell.color_pair_id,
						};
					} else if (cell.char == pipe_corner_4_char) {
						cell_to_print = &Cell(CharType){
							.char = @intFromEnum(constants.CellChar.PIPE_HORIZ),
							.color_pair_id = cell.color_pair_id,
						};
					} else if (cell.char == pipe_end_1_char) {
						cell_to_print = &Cell(CharType){
							.char = @intFromEnum(constants.CellChar.PIPE_HORIZ),
							.color_pair_id = cell.color_pair_id,
						};
					} else {
						cell_to_print = &Cell(CharType){
							.char = ' ',
							.color_pair_id = normal_color_id,
						};
					}
					Self.print_cell(cell_to_print);
				}
				_ = curses.addstr("\n\r");
			}
			_ = curses.refresh();
		}


		pub fn clear(self: *Self) void {
			const cell = Cell(CharType){
				.char = self.empty_char,
				.color_pair_id = self.empty_color_pair_id,
			};

			for (0..self._grid.len) |i| {
				@memset(self._grid[i], cell);
			}
		}


		pub fn set_cell(self: *Self, x: u8, y: u8, char: CharType,
				color_pair_id: u8) void {
			if (x < 0 or x >= self.width or y < 0 or y >= self.height) {
				return;
			}

			self._grid[y][x].char = char;
			self._grid[y][x].color_pair_id = color_pair_id;
		}


		fn print_cell(cell: *const Cell(CharType)) void {
			const char: curses.wchar_t = cell.char;
			const wstr: [2]curses.wchar_t = .{char, 0};
			var cc: curses.cchar_t = curses.cchar_t{};
			_ = curses.setcchar(&cc, &wstr, 0, cell.color_pair_id, null);
			_ = curses.add_wch(&cc);
		}
	};
}


fn Cell(comptime CharType: type) type {
	return struct {
		char: CharType,
		color_pair_id: u8,
	};
}
